/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "PolicyLocality_fp.h"
#include "Marshal.h"


#if CC_PolicyLocality  // Conditional expansion of this file

//  Return Type: TPM_RC
//      TPM_RC_RANGE          all the locality values selected by
//                            'locality' have been disabled
//                            by previous TPM2_PolicyLocality() calls.
/*
tpm2_policylocality(1) - Restricts TPM object authorization to specific TPM locality. Useful when you want to allow only 
specific locality with the TPM object. A locality indicates the source of the command, for example it could be from the 
application layer or the driver layer, each would have it's own locality integer. Localities are hints to the TPM and are 
enforced by the software communicating to the TPM. Thus they are not trusted inputs on their own and are implemented in 
platform specific ways.

As an argument it takes the LOCALITY as an integer or friendly name.

Localities are fixed to a byte in size and have two representations, locality and extended locality.

Localities 0 through 4 are the normal locality representation and are represented as set bit indexes. Thus locality 0 is 
indicated by 1<<0 and locality 4 is indicated by 1<<4. Rather then using raw numbers, these localities can also be specified 
by the friendly names of: - zero: locality 0 or 1<<0 - one: locality 1 or 1<<1 - two: locality 2 or 1<<2 - three: locality 3 
or 1<<3 - four: locality 4 or 1<<4

Anything from the range 32 - 255 are extended localities.

tpm2_policylocality(1) - 将 TPM 对象授权限制到特定的 TPM 位置。 当您只想允许特定位置使用 TPM 对象时很有用。 地点表示命令的来源，
例如它可能来自应用层或驱动层，每个都有自己的地点整数。 位置是 TPM 的提示，由与 TPM 通信的软件强制执行。 因此，它们本身并不是可信的输入，
而是以特定于平台的方式实现的。

作为参数，它将 LOCALITY 作为整数或友好名称。

Localities 的大小固定为一个字节，有两种表示形式，locality 和 extended locality。

位置 0 到 4 是正常的位置表示，并表示为设置位索引。 因此，地点 0 由 1<<0 表示，地点 4 由 1<<4 表示。 除了使用原始数字，还可以通过以下
友好名称指定这些地点： - 零：地点 0 或 1<<0 - 一：地点 1 或 1<<1 - 二：地点 2 或 1<<2 - 三 : 地点 3 或 1<<3 - 四：地点 4 或 1<<4

32 - 255 范围内的任何内容都是扩展位置。


This command indicates that the authorization will be limited to a specific locality.
policySession→commandLocality is a parameter kept in the session context. When the policy session is started, this 
parameter is initialized to a value that allows the policy to apply to any locality.
If locality has a value greater than 31, then an extended locality is indicated. For an extended locality, the TPM 
will validate that policySession→commandLocality has not previously been set or that the current value of 
policySession→commandLocality is the same as locality (TPM_RC_RANGE).
When locality is not an extended locality, the TPM will validate that the policySession→commandLocality is not set to 
an extended locality value (TPM_RC_RANGE). 
If not the TPM will disable any locality not SET in the locality parameter. If the result of disabling localities 
results in no locality being enabled, the TPM will return TPM_RC_RANGE.
If no error occurred in the validation of locality, policySession→policyDigest is extended with
policyDigestnew ≔ HpolicyAlg(policyDigestold || TPM_CC_PolicyLocality || locality)
此命令表示授权将被限制在一个特定的地区。
policySession→commandLocality 是保存在会话上下文中的参数。 当启动策略会话时，此参数被初始化为允许策略应用于任何位置的值。
如果 locality 的值大于 31，则指示扩展的位置。 对于扩展的局部性，TPM 将验证之前未设置 policySession→commandLocality 或 
policySession→commandLocality 的当前值与局部性 (TPM_RC_RANGE) 相同。
当地点不是扩展地点时，TPM 将验证 policySession→commandLocality 是否未设置为扩展地点值 (TPM_RC_RANGE)。
如果不是，TPM 将禁用任何未在 locality 参数中设置的位置。 如果禁用区域的结果导致没有区域被启用，TPM 将返回 TPM_RC_RANGE。
如果在局部性验证中没有发生错误，则将 policySession→policyDigest 扩展为
policyDigestnew ≔ HpolicyAlg（policyDigestold || TPM_CC_PolicyLocality || locality）

*/
TPM_RC
TPM2_PolicyLocality(
    PolicyLocality_In   *in             // IN: input parameter list
    )
{
    SESSION     *session;
    BYTE         marshalBuffer[sizeof(TPMA_LOCALITY)];
    BYTE         prevSetting[sizeof(TPMA_LOCALITY)];
    UINT32       marshalSize;
    BYTE        *buffer;
    TPM_CC       commandCode = TPM_CC_PolicyLocality;
    HASH_STATE   hashState;

// Input Validation

    // Get pointer to the session structure
    session = SessionGet(in->policySession);

    // Get new locality setting in canonical form
    marshalBuffer[0] = 0;   // Code analysis says that this is not initialized
    buffer = marshalBuffer;
    marshalSize = TPMA_LOCALITY_Marshal(&in->locality, &buffer, NULL);

    // Its an error if the locality parameter is zero
    if(marshalBuffer[0] == 0)
        return TPM_RCS_RANGE + RC_PolicyLocality_locality;

    // Get existing locality setting in canonical form
    prevSetting[0] = 0;     // Code analysis says that this is not initialized
    buffer = prevSetting;
    TPMA_LOCALITY_Marshal(&session->commandLocality, &buffer, NULL);

    // If the locality has previously been set
    if(prevSetting[0] != 0
        // then the current locality setting and the requested have to be the same
        // type (that is, either both normal or both extended
        && ((prevSetting[0] < 32) != (marshalBuffer[0] < 32)))
        return TPM_RCS_RANGE + RC_PolicyLocality_locality;

    // See if the input is a regular or extended locality
    if(marshalBuffer[0] < 32)
    {
        // if there was no previous setting, start with all normal localities
        // enabled
        if(prevSetting[0] == 0)
            prevSetting[0] = 0x1F;

        // AND the new setting with the previous setting and store it in prevSetting
        prevSetting[0] &= marshalBuffer[0];

        // The result setting can not be 0
        if(prevSetting[0] == 0)
            return TPM_RCS_RANGE + RC_PolicyLocality_locality;
    }
    else
    {
        // for extended locality
        // if the locality has already been set, then it must match the
        if(prevSetting[0] != 0 && prevSetting[0] != marshalBuffer[0])
            return TPM_RCS_RANGE + RC_PolicyLocality_locality;

        // Setting is OK
        prevSetting[0] = marshalBuffer[0];
    }

// Internal Data Update

    // Update policy hash
    // policyDigestnew = hash(policyDigestold || TPM_CC_PolicyLocality || locality)
    // Start hash
    CryptHashStart(&hashState, session->authHashAlg);

    // add old digest
    CryptDigestUpdate2B(&hashState, &session->u2.policyDigest.b);

    // add commandCode
    CryptDigestUpdateInt(&hashState, sizeof(TPM_CC), commandCode);

    // add input locality
    CryptDigestUpdate(&hashState, marshalSize, marshalBuffer);

    // complete the digest
    CryptHashEnd2B(&hashState, &session->u2.policyDigest.b);

    // update session locality by unmarshal function.  The function must succeed
    // because both input and existing locality setting have been validated.
    buffer = prevSetting;
    TPMA_LOCALITY_Unmarshal(&session->commandLocality, &buffer,
                            (INT32 *)&marshalSize);

    return TPM_RC_SUCCESS;
}

#endif // CC_PolicyLocality